<?php
/**
 * Svick a Fast Simple Smart PHP FrameWork
 * Author: Tommy 863758705@qq.com
 * Link: http://www.Svick.com/
 * Since: 2016
 */

namespace Svick\Orm;


use JsonSerializable;
use PDO;
use Svick\Config\Config;

/**
 * Class Model
 * @package Timo\Core
 */
class Model implements JsonSerializable
{
    /**
     * @var string 数据库类型
     */
    protected $dbType = 'mysql';

    /**
     * @var array 连接参数
     */
    protected $options = [];

    /**
     * @var string 数据库名
     */
    protected $database = 'default';

    /**
     * @var string 数据表名前缀
     */
    protected $prefix = null;

    /**
     * @var string 数据表名
     */
    protected $table = '';

    /**
     * @var Connection 数据库连接
     */
    protected $connection = null;

    /**
     * @var bool 是否启用读写分离
     */
    protected $rwSeparate;

    /**
     * @var static 模型实例集合
     */
    protected static $instances;

    /**
     * @var array 数据
     */
    protected $data;

    /**
     * 构造函数
     */
    public function __construct()
    {
        $conf = Config::runtime($this->dbType . '.' . $this->database);
        if ($this->prefix === null) {
            $this->prefix = $conf['prefix'];
        }
    }

    /**
     * 返回模型单例
     *
     * @return static
     */
    public static function instance()
    {
        $called_class = get_called_class();
        if (!isset(static::$instances[$called_class])) {
            static::$instances[$called_class] = new static();
        }
        return static::$instances[$called_class];
    }

    /**
     * 连接数据库，获取连接实例
     *
     * @param $dbType
     * @param $database
     * @param array $options
     * @return Connection
     */
    protected function connect()
    {
        if ($this->connection !== null) {
            return $this->connection;
        }

        $conf = Config::runtime($this->dbType . '.' . $this->database);
        if (!is_null($this->rwSeparate)) {
            $conf['rw_separate'] = $this->rwSeparate;
        }
        $this->connection = Connection::instance($conf, $this->options);
        return $this->connection;
    }

    /**
     * 获取当前模型所对应的数据表名称（包括表前缀）
     *
     * @return string
     */
    public function getTable()
    {
        if (!$this->table) {
            $class_name = get_class($this);
            $pos = strpos($class_name, 'Model') ? -5 : null;
            $params = [$class_name, strrpos($class_name, '\\') + 1];
            if (!is_null($pos)) {
                $params[] = $pos;
            }
            $table = lcfirst(substr(...$params));
            $table = preg_replace_callback('/[A-Z]?/', function ($match) {
                return !empty($match[0]) ? '_' . strtolower($match[0]) : '';
            }, $table);

            //当有前缀时，加上前缀
            $this->table = (!$this->prefix) ? $table : $this->prefix . $table;
        }

        return $this->table;
    }

    /**
     * 通过主键获取记录
     *
     * @param int|array $id 主键ID
     * @param string $select
     * @return Model
     */
    public static function find($id, $select = '*')
    {
        $model = clone static::instance();
        return $model->connect()
            ->table($model->getTable())
            ->model($model)
            ->mode(PDO::FETCH_ASSOC)
            ->select($select)
            ->where($id)
            ->row();
    }

    /**
     * 获取所有记录
     *
     * @param string $select
     * @return array
     */
    public static function list($select = '*')
    {
        $model = clone static::instance();
        return $model->connect()->table($model->getTable())->select($select)->list();
    }

    /**
     * where条件
     *
     * @param null $column
     * @param null $operator
     * @param null $value
     * @return Query
     */
    public static function where($column = null, $operator = null, $value = null)
    {
        $model = clone static::instance();
        $query = $model->connect()->table($model->getTable())->model($model);
        if (is_null($column)) {
            return $query;
        }
        return $query->where($column, $operator, $value);
    }

    /**
     * 设置whereIn条件
     *
     * @param $column
     * @param $values array|string
     * @return Query
     */
    public static function whereIn($column, $values)
    {
        $model = clone static::instance();
        return $model->connect()->table($model->getTable())->where($column, 'in', $values);
    }

    /**
     * 添加数据
     *
     * @param array $data
     *
     * @return bool|string
     */
    public static function insert(array $data)
    {
        $model = static::instance();
        return $model->connect()->table($model->getTable())->insert($data);
    }

    /**
     * 批量插入数据
     *
     * @param array $data
     * @param bool $return_id
     * @return bool|int
     */
    public static function insertAll(array $data, $return_id = true)
    {
        $model = static::instance();
        return $model->connect()->table($model->getTable())->insertAll($data, $return_id);
    }

    /**
     * 获取数据库连接
     *
     * @return Connection
     */
    public static function connection()
    {
        return static::instance()->connect();
    }

    /**
     * 设置数据
     *
     * @param $data
     * @return Model
     */
    public function data($data)
    {
        $this->data = $data;
        return $this;
    }

    /**
     * 数据是否为空
     *
     * @return bool
     */
    public function isEmpty()
    {
        return empty($this->data);
    }

    /**
     * 数据不为空
     *
     * @return bool
     */
    public function notEmpty()
    {
        return !empty($this->data);
    }

    /**
     * 转换为数组
     *
     * @return array
     */
    public function toArray()
    {
        return $this->data;
    }

    public function __get($name)
    {
        return $this->data[$name] ?? null;
    }

    public function __set($name, $value)
    {
        $this->data[$name] = $value;
    }

    public function __isset($name)
    {
        return isset($this->data[$name]);
    }

    public function __unset($name)
    {
        if (isset($this->data[$name])) {
            unset($this->data[$name]);
        }
    }

    public function jsonSerialize()
    {
        return !empty($this->data) ? $this->data : new \stdClass();
    }
}
